package statement;

import org.junit.Test;
import util.JDBCUtils;

import java.lang.reflect.Field;
import java.sql.*;

/*
* 演示使用PreparedStatement来解决sql注入问题
*
* 除了解决Statement的拼串、sql注入问题之外，PreparedStatement还有那些好处？
* 1.PreparedStatement可以操作Blob的数据，而Statement是做不到的
* 2.preparedStatement可以实现更加高效的批量操作
* */
public class PreparedStatementTest {

    //如何避免出现sql注入：只要用 PreparedStatement(从Statement扩展而来) 取代 Statement

    @Test
    public  void testLogin() {
        /*
         * Junit是自动化的测试，手动的输出会导致一直阻塞，也就是说junit不支持手动输入，
         * 否则会导致当前线程一直阻塞，转圈圈，
         * 所以test类不要用scanner那种控制台手动输入数据的方式，
         * 把数据直接写成形参，测试的时候直接写成参数测试
         * */
        //Scanner scanner = new Scanner(System.in);
        //System.out.print("请输入用户名：");
        String user = "AA";
        //System.out.print("请输入密码：");
        String password = "123456";

        //SELECT user,password FROM user_table WHERE user = '1' or ' AND password = '=1 or '1' = '1'
        String sql = "SELECT user,password FROM user_table WHERE user = ? AND password = ?";
        User returnUser = getInstance(User.class, sql, user, password);
        if (returnUser != null) {
            System.out.println("登录成功");
        } else {
            System.out.println("用户名不存在或密码错误");
        }
    }

    //针对不同的表的通用的查询操作，返回表中的一条记录
    public <T> T getInstance(Class<T> clazz, String sql, Object... args) {

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            //获取连接
            connection = JDBCUtils.getConnection();
            //预编译SQL语句，解决了SQL注入的问题
            preparedStatement = connection.prepareStatement(sql);
            //填充占位符
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            //执行并获取结果集
            resultSet = preparedStatement.executeQuery();
            //获取结果集中列数
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            //处理数据
            if (resultSet.next()) {
                T t = clazz.newInstance();//创建某个类的对象
                for (int i = 0; i < columnCount; i++) {
                    Object value = resultSet.getObject(i + 1);//获取列的值
                    //获取表的别名getColumnLabel()  获取表的列名getColumnName()
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    //给t对象指定的某个属性，赋值为value。(通过反射)
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);//允许访问私有属性
                    field.set(t, value);
                }
                return t;
            }
        } catch (SQLException | IllegalAccessException | NoSuchFieldException | InstantiationException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtils.closeResourceConnection(connection, preparedStatement, resultSet);
        }
        return null;
    }
}
